﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "timescope.h"
#include "CommonServer.h"
#include "servicepacket.h"
#include "RoutineImp.h"
#include "routinedef.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CMasterDBRoutine
IMPLEMENT_RTTI_CREATE( CMasterDBRoutine, CObject, CObjectTraits::OBJECTT_NONE )

UInt CMasterDBRoutine::OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam)
{
    switch (nEvent)
    {
    case PAKE_UNLINK:
        {
            // 将id对应的socket设置为0, 不删除
            SERVER_UNLINK Unlink;  
            Unlink.utData = utData;
            if (Unlink.tiData.usType == PAKT_SERVICE)
            {
                ClearServiceSocket(Unlink.tiData.usId, ullParam);
            }
            else
            {
                assert(Unlink.tiData.usType == PAKT_MASTER);
                ClearMasterSocket(Unlink.tiData.usId, ullParam);
            }
        }
        break;
    case PAKE_SERVICE_MAINTAIN:
        {
            if ((utData == TRUE) || (m_uMaintainEnd > 0))
            {
                CNETSrvMaintainAck MaintainAck;
                MaintainAck.SetType(PAKT_MASTERDB);
                MaintainAck.SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_MASTERDB)->usId);
                CStream& Stream = MaintainAck.Alloc(m_pServer->GetNetworkPtr());
                Stream << m_uMaintainBegin << m_uMaintainEnd;
                if (ullParam != 0)
                {
                    MaintainAck.Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
                }
                else
                {
                    MaintainAck.BroadcastSend(m_pServer->GetNetworkPtr(), CNETTraits::SEND_BROADCAST_AS, PAKT_MASTER_MASTERDB);
                }
            }
            return RET_OKAY;
        }
        break;
    default: {}
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, utData, ullParam);
    }
    return RET_OKAY;
}

UInt CMasterDBRoutine::OnHandle(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, EventRef, ullParam);
    }
    return RET_OKAY;
}

UInt CMasterDBRoutine::OnHandle(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, Stream, ullParam);
    }
    return RET_OKAY;
}

bool CMasterDBRoutine::Start(ICommonServer* pServer)
{
    #pragma message("[业务功能例程]未支持多业务编号")
    m_pServer = pServer;
    if ((m_pServer != nullptr) && LoadExtConfig())
    {
        // 默认必须创建异步事件队列
        m_uThreads = (UInt)m_pServer->GetConfig().GetServerValue(CServerConfig::CFG_LOAD_MASTERDB);
        m_uThreads = CPlatform::CheckThread(m_uThreads);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务数据例程]异步事件队列处理线程=%d, %s"), m_uThreads, (m_uThreads > 1) ? TF("启用多线程并发处理") : TF("启用单线程处理"));
        m_nUpdate         = 0;
        // 超时为更新周期的倍数
        SetTimeout();

        m_ServiceId.Init();
        m_MasterId.Init();

        if (m_BusinessPtr != nullptr)
        {
            if (m_BusinessPtr->Start(pServer) == false) // 注意异步事件队列还未初始化
            {
                return false;
            }
        }

        if (CEventQueue::EventQueue(m_EventQueuePtr, *this, 0, (Int)m_uThreads))
        {
            m_EventQueuePtr->SetThreadEvent(PAKE_THREAD_START, PAKE_THREAD_STOP);
            return m_EventQueuePtr->Init();
        }
    }
    return false;
}

bool CMasterDBRoutine::Pause(bool bPause)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Pause(bPause);
    }
    return false;
}

bool CMasterDBRoutine::Update(void)
{
    if (m_pServer->GetServerInfo(PAKT_MASTERDB)->IsMayDay() == false)
    {
        if (m_EventQueuePtr->CheckThreads(m_uTimeout))
        {
            m_pServer->GetServerInfo(PAKT_MASTERDB)->MayDay();
            DEV_ERROR(TF("[业务数据例程]事件队列线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[业务数据例程]事件队列线程异常, 进入异常状态"));
        }
    }
    // 定时更新线程不需要检测
    // 定时更新线程异常导致不向同步管理发送心跳包, 会导致本服务器离线

    // 定时检测扩展配置文件是否有修改
    ++m_nUpdate;
    if (m_nUpdate >= m_nTime)
    {
        m_nUpdate = 0;

        CTime tCurr;
        if (m_uMaintainEnd <= (UInt)tCurr.GetTime())
        {
            m_uMaintainBegin = 0;
            m_uMaintainEnd   = 0;
        }

        LoadExtConfig(true);
    }
    if (m_bUpdateMaintain)
    {
        m_bUpdateMaintain = false;
        // TRUE强制发送, 0表示广播
        m_EventQueuePtr->Add(PAKE_SERVICE_MAINTAIN, TRUE, 0);
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Update();
    }
    return true;
}

void CMasterDBRoutine::Stop(void)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Stop();
        CString strName = m_BusinessPtr->GetRTTI().GetName();
        m_BusinessPtr->Exit();
        m_BusinessPtr = nullptr;
        CObjectTraits::Unload<ICommonRoutine>(*strName);
    }
    if (m_EventQueuePtr != nullptr)
    {
        m_EventQueuePtr->Exit();
        m_EventQueuePtr = nullptr;
    }
    m_ServiceId.Exit();
    m_MasterId.Exit();

    m_pServer         = nullptr;
    m_nUpdate         = 0;
    m_uThreads        = 0;
    m_uTimeout        = 0;
    m_uMaintainBegin  = 0;
    m_uMaintainEnd    = 0;
    m_bUpdateMaintain = false;
}

Socket_t CMasterDBRoutine::GetSocket(UInt uType, UInt uId)
{
    if (uType == PAKT_SERVICE)
    {
        ID_MAP_PAIR* pPair = m_ServiceId.GetCurr().Find(uId);
        if (pPair != nullptr)
        {
            return (Socket_t)pPair->m_V;
        }
    }
    else
    {
        assert(uType == PAKT_MASTER);
        ID_MAP_PAIR* pPair = m_MasterId.GetCurr().Find(uId);
        if (pPair != nullptr)
        {
            return (Socket_t)pPair->m_V;
        }
    }
    return 0;
}

bool CMasterDBRoutine::SetTimeout(UInt uTimeout)
{
    if (uTimeout == 0)
    {
        m_uTimeout = m_pServer->GetConfig().GetUpdateTick() * DATAD_TIME;
    }
    else
    {
        m_uTimeout = uTimeout;
        m_uTimeout = DEF::Max<UInt>(1, m_uTimeout);
    }
    return true;
}

bool CMasterDBRoutine::AddQueue(Int nEvent, uintptr_t utData, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerAck"
    // PAKE_LINKACK/PAKE_UNLINKACK, id, center
    // "ServerInfo"
    // PAKE_UNLINK, PSERVER_STATUS(id, status-live-data), socket
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, utData, ullParam, eType);
    }
    switch (nEvent)
    {
    case PAKE_LINKACK:    // 向管理服务器注册回复结果
    case PAKE_UNLINK:     // 向本服务器注册的注销/断开
    case PAKE_UNLINKACK:  // 向管理服务器注销/断开回复结果
    case PAKE_TCP_CLOSED: // 连接的外网端注销/断开
        {
            bRet = true;
        }
        break;
    default: {}
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, utData, ullParam, eType);
    }
    return bRet;
}

bool CMasterDBRoutine::AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerInfo"
    // PAKE_LINK/PAKE_UPDATE, EventRef, PTCP_PARAM
    // "CommonEvent"
    // [PAKE_COMMON_BEGIN, PAKE_COMMON_END], EventRef, PTCP_PARAM
    //
    // [PAKE_SERVICE_BEGIN, PAKE_SERVICE_END], EventRef, PTCP_PARAM
    // [PAKE_ROUTINE_BEGIN, PAKE_ROUTINE_FAIL], EventRef, PTCP_PARAM
    bool bRet = true;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, EventRef, ullParam, eType);
    }

    ULLong ullSocket = ullParam;
    if (eType > QUEUE_TYPE_MASK)
    {
        CNETTraits::PTCP_PARAM pTcp = reinterpret_cast<CNETTraits::PTCP_PARAM>(ullParam);
        ullSocket = pTcp->sSocket;
    }
    if ((nEvent >= PAKE_ROUTINE_BEGIN) && (nEvent <= PAKE_ROUTINE_FAIL))
    {
        bRet = true;
        (static_cast<CNETRoutine*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    else if ((nEvent >= PAKE_SERVICE_BEGIN) && (nEvent <= PAKE_SERVICE_END))
    {
        bRet = true;
        (static_cast<CNETSession*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, EventRef, ullSocket, (eType & QUEUE_TYPE_MASK));
    }
    if (nEvent == PAKE_LINK)
    {
        CNETLink* pLink = static_cast<CNETLink*>(&EventRef);
        if (pLink->GetType() == PAKT_SERVICE)
        {
            bRet = SetServiceSocket(pLink->GetServerStatus().usId, ullSocket);
        }
        else
        {
            assert(pLink->GetType() == PAKT_MASTER);
            bRet = SetMasterSocket(pLink->GetServerStatus().usId, ullSocket);
        }
        if (bRet)
        {
            if ((m_uMaintainEnd > 0) && (pLink->GetType() == PAKT_MASTER))
            {
                m_EventQueuePtr->Add(PAKE_SERVICE_MAINTAIN, 0, ullSocket);
            }
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务数据例程]%p服务器类型[%d]注册时对应的id=%d已经有服务器在线"), ullSocket, pLink->GetType(), pLink->GetServerStatus().usId);
            pLink->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
            pLink->ResetHead((UEVENT)PAKE_LINKACK, (UTYPE)PAKT_MASTERDB, (UACK)ROUTINE_ERROR_VOIDNESS_SRVID);
            pLink->Send(ullSocket, m_pServer->GetNetworkPtr());
        }
    }
    return bRet;
}

bool CMasterDBRoutine::AddQueue(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "SyncInfo"---注意流对象可能是临时实例, 不能加入队列
    // PAKE_LINK/PAKE_UPDATE, Stream, center
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, Stream, ullParam);
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, Stream, ullParam);
    }
    return bRet;
}

bool CMasterDBRoutine::LoadExtConfig(bool bUpdate)
{
    CStringFix strPath;
    m_pServer->GetConfig().GetServerExtConfig(CServerConfig::CFG_LOAD_MASTERDB, strPath);

    CFileAttr fa;
    m_pServer->GetConfig().GetFilePath().FileAttr(fa, *strPath);
    if (bUpdate)
    {
        if (fa.MTime(m_tExtConfig))
        {
            return true;
        }
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务数据例程]定时读取文件修改时间%llX---%llX"), fa.MTime(), m_tExtConfig);
        m_tExtConfig = fa.MTime();
    }
    else
    {
        m_tExtConfig = fa.MTime();
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务数据例程]初始化读取文件修改时间=%llX"), m_tExtConfig);
    }
    LOGV_INFO(m_pServer->GetFileLog(), TF("[业务数据例程]ExtConfig=%s"), *strPath);

    CFileReadStream Reader;
    if (Reader.Create(*strPath))
    {
        CKVNode  KVNode;
        CKVStore KVStore;
        if (KVStore.Load(KVNode, Reader))
        {
            if (KVNode.Find(TF("Time")))
            {
                m_nTime = KVNode[TF("Time")].GetInt();
            }
            CTime tCurr;
            UInt uBegin = m_uMaintainBegin;
            UInt uEnd   = m_uMaintainEnd;
            if (KVNode.Find(TF("MaintainBegin")))
            {
                uBegin = (UInt)KVNode[TF("MaintainBegin")].GetULong();
            }
            if (KVNode.Find(TF("MaintainEnd")))
            {
                uEnd = (UInt)KVNode[TF("MaintainEnd")].GetULong();
            }
            if ((uBegin > (UInt)tCurr.GetTime()) && (uEnd > uBegin) && (uEnd != m_uMaintainEnd))
            {
                m_uMaintainBegin  = uBegin;
                m_uMaintainEnd    = uEnd;
                m_bUpdateMaintain = bUpdate;
            }

            bool bRet = true;
            if ((bUpdate == false) && (KVNode.Find(TF("BusinessRoutine"))))
            {
                bRet = LoadExtBusiness(KVNode);
            }
            if (m_BusinessPtr != nullptr)
            {
                bRet = (m_BusinessPtr->OnHandle(bUpdate ? PAKE_CONFIG_UPDATE : PAKE_CONFIG_LOAD,
                                                reinterpret_cast<uintptr_t>(&KVNode),
                                                (ULLong)m_pServer) == RET_OKAY);
            }
            return bRet;
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务数据例程]解析ExtConfig=%s文件内容失败"), *strPath);
        }
    }
    else
    {
        LOGV_WARN(m_pServer->GetFileLog(), TF("[业务数据例程]打开ExtConfig=%s文件不存在"), *strPath);
    }
    return true;
}

bool CMasterDBRoutine::LoadExtBusiness(CKVNode& Root)
{
    CKVString strName;
    CKVString strObject;
    CKVString strModule;

    CKVNode& Routine = Root[TF("BusinessRoutine")];
    Routine[TF("Name")].GetString(strName);
    Routine[TF("Object")].GetString(strObject);
    Routine[TF("Module")].GetString(strModule);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    strModule.Insert(0, CServerConfig::LibPrefix);
#endif
    strModule += CServerConfig::LibExt;

    assert(m_BusinessPtr == nullptr);
    if (CObjectTraits::Load<ICommonRoutine>(*strName, *strModule, m_BusinessPtr, *strObject))
    {
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务数据例程]RTTI load --- Object=%s, Name=%s, Module=%s --- %p"), *strObject, *strName, *strModule, m_BusinessPtr.Get());
        if (m_BusinessPtr->Init() == (UInt)RET_OKAY)
        {
            m_BusinessPtr->OnHandle(PAKE_CONFIG_DEFAULT, reinterpret_cast<uintptr_t>(this), (ULLong)m_pServer);
            return true;
        }
    }
    LOGV_ERROR(m_pServer->GetFileLog(), TF("[业务数据例程]RTTI load --- Object=%s, Name=%s, Module=%s --- Failed!"), *strObject, *strName, *strModule);
    return false;
}

bool CMasterDBRoutine::SetServiceSocket(UInt uId, ULLong ullParam)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_ServiceId.Lock);
    ID_MAP& ServiceId = m_ServiceId.GetNextClone();

    ID_MAP_PAIR* pPair = ServiceId.Find(uId);
    if (pPair != nullptr)
    {
        if (pPair->m_V == 0)
        {
            pPair->m_V = ullParam;
        }
        else
        {
            bRet = false;
        }
    }
    else
    {
        bRet = (ServiceId.Add(uId, ullParam) != nullptr);
    }
    if (bRet)
    {
        m_ServiceId.Swap();
    }
    return bRet;
}

bool CMasterDBRoutine::ClearServiceSocket(UInt uId, ULLong ullParam)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_ServiceId.Lock);
    ID_MAP& ServiceId = m_ServiceId.GetNextClone();

    ID_MAP_PAIR* pPair = ServiceId.Find(uId);
    if (pPair != nullptr)
    {
        pPair->m_V = 0;
    }
    else
    {
        bRet = false;
    }
    if (bRet)
    {
        m_ServiceId.Swap();
    }
    return bRet;
}

bool CMasterDBRoutine::SetMasterSocket(UInt uId, ULLong ullParam)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_MasterId.Lock);
    ID_MAP& MasterId = m_MasterId.GetNextClone();

    ID_MAP_PAIR* pPair = MasterId.Find(uId);
    if (pPair != nullptr)
    {
        if (pPair->m_V == 0)
        {
            pPair->m_V = ullParam;
        }
        else
        {
            bRet = false;
        }
    }
    else
    {
        bRet = (MasterId.Add(uId, ullParam) != nullptr);
    }
    if (bRet)
    {
        m_MasterId.Swap();
    }
    return bRet;
}

bool CMasterDBRoutine::ClearMasterSocket(UInt uId, ULLong ullParam)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_MasterId.Lock);
    ID_MAP& MasterId = m_MasterId.GetNextClone();

    ID_MAP_PAIR* pPair = MasterId.Find(uId);
    if (pPair != nullptr)
    {
        pPair->m_V = 0;
    }
    else
    {
        bRet = false;
    }
    if (bRet)
    {
        m_MasterId.Swap();
    }
    return bRet;
}

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
// Windows Realse版本MSVC编译器如果不强制调用静态常量, 静态常量会被优化掉
BOOL WINAPI DllMain(HMODULE hModule, ULong ulReason, void* lpReserved)
{
    UNREFERENCED_PARAMETER( lpReserved );

    if (ulReason == DLL_PROCESS_ATTACH)
    {
        ::DisableThreadLibraryCalls(hModule);
        DEV_DUMP(TF("$ Link %s.dll --- DLL_PROCESS_ATTACH"), CMasterDBRoutine::ms_RTTI.GetName());
    }
    else if (ulReason == DLL_PROCESS_DETACH)
    {
        DEV_DUMP(TF("$ Unlink %s.dll --- DLL_PROCESS_DETACH"), CMasterDBRoutine::ms_RTTI.GetName());
    }
    return TRUE;
}
#endif
